05 - Wykrywanie krawędzi i konturów, dopasowywanie cech

Przetwarzanie i Analiza Obrazów

Politechnika Poznańska, Instytut Robotyki i Inteligencji Maszynowej

Ćwiczenie laboratoryjne 5: Wykrywanie krawędzi i konturów, dopasowywanie cech.

Powrót do spisu treści ćwiczeń laboratoryjnych

W tym ćwiczeniu:

1. Cel ćwiczenia

Celem ćwiczenia jest nauczenie się wykrywania i analizy krawędzi oraz konturów w obrazach oraz prostych metod dopasowywania cech między obrazami.

2. Wykrywanie krawędzi

Wykrywanie krawędzi to podstawowy krok w wielu algorytmach analizy obrazu. Popularne metody obejmują operatory Sobela, Prewitta, Laplace, a w praktycznych zastosowaniach najczęściej używa się algorytmu Canny.

Operatory Sobela i Prewitta

Operatory Sobela i Prewitta to klasyczne filtry gradientowe pokazujące kierunki i siłę krawędzi. Sobel korzysta z wag, które dają większe znaczenie centralnym pikselom; Prewitt można zaimplementować jako konwolucję z odpowiednimi kernelami.

Przykłady:

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
img_blur = cv2.GaussianBlur(img, (3,3), 0)

# Filtr Prewitta
kernelx = np.array([
  [-1,0,1],
  [-1,0,1],
  [-1,0,1]], dtype=np.float32)
kernely = np.array([
  [1,1,1],
  [0,0,0],
  [-1,-1,-1]], dtype=np.float32)

prewittx = cv2.filter2D(img_blur, -1, kernelx)
prewitty = cv2.filter2D(img_blur, -1, kernely)
prewitt_mag = cv2.convertScaleAbs(np.sqrt(np.float32(prewittx)**2 + np.float32(prewitty)**2))

cv2.imshow('Prewitt X', prewittx)
cv2.imshow('Prewitt Y', prewitty)
cv2.imshow('Prewitt Magnitude', prewitt_mag)
cv2.waitKey(0)
cv2.destroyAllWindows()

# Filtr Sobela
sobelx = np.array([
  [-1,0,1],
  [-2,0,2],
  [-1,0,1]], dtype=np.float32)
sobely = np.array([
  [1,2,1],
  [0,0,0],
  [-1,-2,-1]], dtype=np.float32)

sobelx = cv2.filter2D(img_blur, -1, sobelx)
sobely = cv2.filter2D(img_blur, -1, sobely)
sobel_mag = np.sqrt(sobelx**2 + sobely**2)
sobel_mag = np.uint8(np.clip(sobel_mag / np.max(sobel_mag) * 255, 0, 255))

cv2.imshow('Sobel X', sobelx)
cv2.imshow('Sobel Y', sobely)
cv2.imshow('Sobel Magnitude', sobel_mag)
cv2.waitKey(0)
cv2.destroyAllWindows()

Wskazówki:

Algorytm Canny

Algorytm Canny (dwustopniowe progowanie) składa się z: wygładzenia (zwykle filtr Gaussa), obliczenia gradientów, nie-maksimum suppression oraz hysteresis thresholding (dwa progi: niski i wysoki).

Przykład użycia:

import cv2

img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
blur = cv2.GaussianBlur(img, (5,5), 1.4)
edges = cv2.Canny(blur, threshold1=50, threshold2=150)

cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

Wskazówki: - Dostosuj progi threshold1 i threshold2 do kontrastu obrazu (zwykle threshold2 ≈ 2-3 × threshold1).

3. Znajdowanie i analiza konturów

Kontury to krzywe ograniczające obszary o tej samej intensywności (zwykle po binarizacji). W OpenCV używa się cv2.findContours. Fantastyczne narzędzie do analizy kształtów, rozmiarów i relacji przestrzennych obiektów.

Podstawowe kroki:

  1. Przygotuj obraz binarny (np. progowanie, Canny).

  2. Wywołaj contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE).

  3. Analizuj kontury: cv2.contourArea, cv2.arcLength, cv2.boundingRect, cv2.minEnclosingCircle, cv2.approxPolyDP, cv2.moments.

Przykład:

import cv2
import numpy as np

img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Sortowanie konturów po polu (malejąco)
contours_sorted = sorted(contours, key=cv2.contourArea, reverse=True)

for i, cnt in enumerate(contours_sorted[:5]):
  area = cv2.contourArea(cnt)
  x,y,w,h = cv2.boundingRect(cnt)
  approx = cv2.approxPolyDP(cnt, 0.02*cv2.arcLength(cnt, True), True)
  M = cv2.moments(cnt)
  if M['m00'] != 0:
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
  else:
    cx, cy = 0, 0
  cv2.drawContours(img, [cnt], -1, (0,255,0), 2)
  cv2.circle(img, (cx,cy), 3, (0,0,255), -1)

cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Hierarchia konturów (argument hierarchy) pozwala rozróżnić kontury zagnieżdżone (np. obiekty i otwory). hierarchy to macierz, gdzie dla każdego konturu mamy indeks następnego, poprzedniego, pierwszego potomka i rodzica.

Wskazówki i zastosowania:

4. Dopasowywanie cech — Template Matching (dopasowywanie szablonu)

Template matching to prosty sposób na znalezienie małego wzorca w większym obrazie. OpenCV oferuje cv2.matchTemplate, która zwraca mapę podobieństwa; następnie używamy cv2.minMaxLoc aby znaleźć najlepsze dopasowania.

Przykład prosty:

import cv2
import numpy as np

img = cv2.imread('image.jpg', cv2.IMREAD_COLOR)

template = img[500:600, 500:600]  # Przykładowy fragment obrazu jako szablon
th, tw = template.shape[:2]

res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

# Dla metody TM_SQDIFF najlepszy wynik to min_loc
top_left = max_loc
bottom_right = (top_left[0] + tw, top_left[1] + th)
cv2.rectangle(img, top_left, bottom_right, (0,255,0), 2)

cv2.imshow('Selected Template', template)
cv2.imshow('Template Matching', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Wskazówki:

5. Zadania do samodzielnego wykonania

Każde zadanie wykonaj w osobnym pliku Python (zad1.py, zad2.py, …).

💥 Zadanie 1 - Wykrywanie krawędzi w HSV 💥

💥 Zadanie 2 - Dopasowywanie szablonu 💥

💥 Zadanie 3 - Zadanie otwarte 💥

Policz automatycznie jaka kwota znajduje się na obrazie coins.jpg. Sumę monet wyświetl w terminalu z dokładnością do 2 miejsc po przecinku, wykorzystując f-string. Możesz wykorzystać dowolne metody (np. wykrywanie krawędzi, konturów, dopasowywanie szablonu) — ważne jest, aby program był w stanie rozróżnić monety o różnych nominałach i zsumować ich wartość.